package math.common;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MathCommon {
	
	private MathCommon(){};
	
	public static Double getCorrelation (List<Double> x, List<Double> y) {
		Double sum = 0.0;
		Double mX = 0.0;
		Double mY = 0.0;
		Double dX = 0.0;
		Double dY = 0.0;
		int n = x.size();
		for (int i = 0; i < n; i++ ) {
			mX += x.get(i);
			mY += y.get(i);
		}
		mX = mX/n;
		mY = mY/n;
		for (int i = 0; i < n; i++ ) {
			sum += (x.get(i)-mX)*(y.get(i)-mY);
			dX += Math.pow(x.get(i)-mX, 2.0);
			dY += Math.pow(y.get(i)-mY, 2.0);
		}
		return sum/(Math.sqrt(dX*dY));
	}
	
	private static Integer index(Double v, List<Double> x) {
		for (int i = 0; i < x.size(); i++ ) {
			if (Math.abs(v - x.get(i))<0.0001) 
				return i;
		}
		return null;
	}
	 
	public static Double getSpirmanCorrelation (List<Double> x, List<Double> y) {
		List<Double> sortX = new ArrayList<Double>(); 
		sortX.addAll(x); 
		Collections.sort(sortX);
		List<Double> sortY = new ArrayList<Double>();
		sortY.addAll(y); 
		Collections.sort(sortY);
		int n = x.size();
		Double d = 0.0;
		for (int i = 0; i < n; i++) {
			d += Math.pow(index(x.get(i),sortX)-index(y.get(i),sortY),2.0);
		}
		return 1 - (double) 6*d/(n*(n*n-1));
	}
	public static Double get4Moment(ArrayList<Double> x) {
		
		Double sumU4 = 0.0;
		int n = x.size();
		for (int i=0; i<n; i++) {
			sumU4 += Math.pow(x.get(i), 4.0);
		}
		return sumU4/n;
		
	}
	
	public static Double getUnb4Moment(ArrayList<Double> x) {
		//x - centered variables
		Double sumU4 = 0.0;
		Double sumU2 = 0.0;
		int n = x.size();
		for (int i=0; i<n; i++) {
			sumU2 += Math.pow(x.get(i), 2.0);
			sumU4 += Math.pow(x.get(i), 4.0);
		}
		int nn = (n-1)*(n-2)*(n-3);
		return sumU4*(n*n-2*n+3)/nn + 3*(2*n-3)*sumU2*sumU2/(nn*n);
		
	}
	
	public static Double getVar (List<Double> x) {
		Double mX = 0.0;
		Double dX = 0.0;
		int n = x.size();
		for (int i = 0; i < n; i++ ) {
			mX += x.get(i);
		}
		mX = mX/n;
		for (int i = 0; i < n; i++ ) {
			dX += Math.pow(x.get(i)-mX, 2.0);
		}
		return dX/(n-1);
		
	}
	
	public static Double getMean (List<Double> x) {
		Double s = 0.0;
		for (Double xi : x) {
			s += xi;
		}
		return s/x.size();
	}
	
	public static Map<Double, Double> Normalize(Map<Double, Double> V) {
		Double meanArg = 0.0;
		Double meanV = 0.0;
		for (Double arg : V.keySet()) {
			meanArg += arg;
			meanV += V.get(arg);
		}
		meanArg /= V.size();
		meanV /= V.size();
		Map<Double, Double> normV = new HashMap<Double, Double>();
		for (Double arg : V.keySet()) {
			normV.put(arg-meanArg, V.get(arg)-meanV);
		}
		return normV;
		
	}
	
	public static Double quantile (List<Double> values, Double p) {
		
		double percentile;
    	int i,n = values.size();
    	List<Double> orderedVals = values;
    	List<Double> probs = new ArrayList<Double>();
    	Collections.sort(orderedVals);
    	for (i=0; i<n; i++) 
    		//probs.add(100.0/n*(i-0.5));
    		probs.add(1./n*(i+1-0.5));
    	if (p < probs.get(0)) return orderedVals.get(0);
    	if (p > probs.get(n-1)) return orderedVals.get(n-1);
    	i=1;
    	while (probs.get(i)<p)
    		i++;
    	percentile = orderedVals.get(i-1)+(p-probs.get(i-1))*(orderedVals.get(i)-orderedVals.get(i-1))*n;
    	return percentile;
	}

}
